package catchnthrow;

/**
 * This Class throws the ball somewhere - depending on its size
 * @author mb
 * Function:
 * - Get the ball on an position on the beam (BallPosition)
 * - accelerate the ball using a beam position (BeamPos1)
 * - Wait (ball rolls to the other side): WaitTime
 * - Get the beam up to BeamPos2 throw the ball
 * - Wait some time (WaitTimeEnd)
 */
public class Throw extends Thread {
	
	public final int BigBall 	= 2;
	public final int MediumBall	= 1;	
	public final int SmallBall 	= 0;
	private int BallSize = BallAndBeamRegul.unknownBall; //Added by Lorenz
	
	public RunningMonitor runMon;
	
	DoneMonitor done;
	int plotter;
	
	// Sampling time of the throw (state machine...) in milliseconds
	private static final long samplingRateThrow = 2;
	
	// Max. Beam/Ball positioning error
	private static final double beamError = 0.1;
	private static final double ballError = 0.05;
	
	// Throw parameters for the different balls
	private ThrowParameters[] Parameters = new ThrowParameters[3];

	// Controller
	Beam beamController;
	BallAndBeamRegul ballController;
	
	/**
	 * Constructor
	 * @param inA analog input of current angle setting
	 * @param inP analog input of current ball position
	 * @param outM analog output to motor
	 * @param pri Priority of the thread
	 */
	public Throw(Beam beam, BallAndBeamRegul bb, int pri) {
		setName("ThrowProcess"); //For debugging purposes

		beamController = beam;
		ballController = bb;
		
		done = new DoneMonitor();
		
		Parameters[BigBall] = new ThrowParameters();
		Parameters[MediumBall] = new ThrowParameters();
		Parameters[SmallBall] = new ThrowParameters();
		
		setBallParam(0,-7,-7,2000,0,BigBall);
		setBallParam(-5,-7,4,600,1000,MediumBall);
		setBallParam(7,9,-8,490,100,SmallBall);
		runMon = new RunningMonitor();
		runMon.setRunning(false);
		
		setPriority(pri);
	}
	
	public synchronized void setBallParam(double BallPosition, double BeamPos1, double BeamPos2, long WaitTime, long WaitTimeEnd, int Ball) {
		if (Ball< 3) {
			Parameters[Ball].BallPosition = BallPosition;
			Parameters[Ball].BeamPos1 = BeamPos1;
			Parameters[Ball].BeamPos2 = BeamPos2;
			Parameters[Ball].WaitTime = WaitTime;
			Parameters[Ball].WaitTimeEnd = WaitTimeEnd;
		}
	}
	
	/**
	 * To set the whole parameter Array
	 * @author Lorenz
	 */
	public void setBallParam(ThrowParameters[] newParam){
		Parameters = (ThrowParameters[]) newParam.clone();
	}
	
	/**
	 * Return current Parameter settings
	 * @author Lorenz
	 */
	public ThrowParameters[] getBallParameters(){
		return Parameters;
	}
	
	/**
	 * Sets the BallSize. Save default: BigBall (simply drops the Ball)
	 * @param Size (Big = 2; Medium = 1; Small = 0)
	 */
	public void setBallSize(int Size) {
		switch (Size) {
			case BigBall:
				BallSize = BigBall;
			break;
			case MediumBall:
				BallSize = MediumBall;
			break;
			case SmallBall:
				BallSize = SmallBall;
			break;
			default:
				BallSize = BigBall;
			break;		
		}
	}
	
	/**
	 * Returns current recognized Ballsize
	 * @author Lorenz
	 */
	public int getBallSize() {
			return BallSize;
	}
	
	/**
	 * Monitor class for the "done" status variable
	 * @author schlupp
	 *beamController.shutDown();
	 */
	class DoneMonitor {
		boolean done = false;
		public synchronized boolean isDone() {
			return done;
		}

		public synchronized void setDone(boolean done) {
			this.done = done;
		}	
	}
	class RunningMonitor {
		boolean running;

		public synchronized boolean isRunning() {
			return running;
		}

		public synchronized void setRunning(boolean running) {
			this.running = running;
		}
	}
	
	public void stopThrow() {
		runMon.setRunning(false);
		beamController.shutDown();
		ballController.shutDown();
	}
	
	public void restart() {
		done.setDone(false);
		runMon.setRunning(true);
	}
	
	/**
	 * returns what should be plotted: 0: Nothing, 1: Beam, 2: Ball
	 */
	public int getPlotter() {
		return plotter;
	}
	
	/**
	 * Run method
	 */
	public void run() {
		long t = System.currentTimeMillis();
		long duration;
		
		int state = 0;
		int counter= 0;
		
		synchronized(this) {
			while (true) {
				if (runMon.isRunning()) {
					counter++;
					if (counter == 1) {
						switch (state) {
							case 0:
								ballController.setRef(Parameters[BallSize].BallPosition);
								ballController.restart();
								plotter = 2;
							break;
							case 1:
								ballController.shutDown();
								beamController.setRef(Parameters[BallSize].BeamPos1);
								beamController.restart();
								plotter = 1;
							break;
							case 2:
								
							break;
							case 3:
								beamController.setRef(Parameters[BallSize].BeamPos2);
								beamController.restart();
							break;
							case 4:
								plotter = 0;
							break;	
							default:
								beamController.shutDown();
								ballController.shutDown();
							break;
						}
					}
					switch (state) {
						case 0: // Bring Ball to a certain position
							if (ballController.hasReached(ballError)) {
								state++;
								counter = 0;
							}
						break;
						case 1: // Move Beam to accelerate the ball
								state++;
								counter = 0;
						break;
						case 2: //Waiting
							if (Parameters[BallSize].WaitTime/samplingRateThrow < counter) {
								state++;
								counter = 0;
							}
						break;
						case 3: // Move beam again to throw
							if (beamController.hasReached(beamError)) {
								state++;
								counter = 0;
							}
						break;
						case 4: //Wait a little
							if (Parameters[BallSize].WaitTimeEnd/samplingRateThrow < counter) {
								state++;
								counter = 0;
								done.setDone(true);
								state = 0;
								stopThrow();
							}
						break;	
						default:
						break;
					}
				}
			t += samplingRateThrow;
			duration = t-System.currentTimeMillis();
			if ( duration > 0) {
				try {
					sleep(duration);
				} catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			}						
		}	
	}
		
}
